package com.cw.part3.service;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * 用redis实现分布式锁
 *
 *  给变量加锁：setIfAbsent(key,value)
 *          特点：可以对同一个key-value执行多次set操作，但是，只有在第一次执行这个方法时，才会返回true，其他时候执行都会返回False
 *  释放锁：delete(key)
 *  获取锁：get(key)
 */
@Component
public class RedisLock {
    @Autowired
    private StringRedisTemplate redisTemplate;

    //这种实现方式，虽然可以很好的解决这种高并发问题，但是有一个新的问题：
    // 如果业务逻辑报错了，那么将会出现其他同一时间进来的请求都会失败。因为没释放，则获取相同key-value的其他同时进行的线程将会直接失败（死锁）
    public boolean lock1(String key, String value) {
        //setIfAbsent(key, value) 给这个key-value加锁，如果加锁成功，是返回true，否则 ，返回false
        if (redisTemplate.opsForValue().setIfAbsent(key,value)) {
            //第一次，即：这个key还没有被赋值的时候
            return true;
        }
        return false;
    }


    public void unlock(String key, String value) {
        try {

            if (redisTemplate.opsForValue().get(key).equals(value)) {
                redisTemplate.opsForValue().getOperations().delete(key);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public boolean lock3(String key, String value) {
        //setIfAbsent相当于jedis中的setnx，如果能赋值就返回true，如果已经有值了，就返回false
        //即：在判断这个key是不是第一次进入这个方法
        if (redisTemplate.opsForValue().setIfAbsent(key, value)) {
            //第一次，即：这个key还没有被赋值的时候
            return true;
        }




        String current_value = redisTemplate.opsForValue().get(key);
        if (!current_value.equals("")
                //超时了
                && Long.parseLong(current_value) < System.currentTimeMillis()) {
            String old_value = redisTemplate.opsForValue().getAndSet(key, value);
            if (!old_value.equals("")
                    && old_value.equals(current_value)) {
                return true;
            }
        }





        return false;
    }


//    private String Object2String(Object o) {
//        if (o == null) {
//            return "";
//        } else {
//            return o.toString();
//        }
//    }
}
